<template>
	<div class="base-content" ref="baseContent" v-loading="loading" id="test1">
		<el-row :gutter="10">
			<el-col :span="12">
				<el-card>
					<template #header>
						<span>第一步：选择图片</span>
					</template>
					<el-upload
						ref="uploader"
						:class="{ hide: fileList.length > 0 ? true : false }"
						:file-list="fileList"
						action="https://jsonplaceholder.typicode.com/posts/"
						list-type="picture-card"
						accept=".jpg, .jpeg, .png"
						:auto-upload="false"
						:limit="1"
						:on-change="handleChange"
						:on-remove="handleRemove"
					>
						<el-icon>
							<plus />
						</el-icon>
						<template #file="{ file }">
							<div>
								<img class="el-upload-list__item-thumbnail" ref="upldf" :src="file.url" @load="imgLoad" />
								<span class="el-upload-list__item-actions">
									<span class="el-upload-list__item-delete" @click="handleRemove">
										<el-icon style="font-size: 1.3em;">
											<delete />
										</el-icon>
									</span>
								</span>
							</div>
						</template>
					</el-upload>
				</el-card>
			</el-col>
		</el-row>
		<el-affix :offset="80">
			<el-alert show-icon type="warning" :closable="false" v-if="fileList.length > 0">
				<template #title>点击下面画布内容可获取对应像素信息</template>
				<span v-if="curPoint.x != null">
					<span style="margin-right: 1em;">当前点击坐标({{ curPoint.x }}, {{ curPoint.y }})</span>
					<span class="color-temp" :style="{ background: curPoint.rgbaColor+'' }"></span>
					<span style="margin-left: 1em;">{{ curPoint.rgbaColor }}</span>
					<span style="margin-left: 1em;">{{ curPoint.hexColor }}</span>
				</span>
			</el-alert>
		</el-affix>
		<div class="canvas-container" :style="{ 'height': cheight + 'px', 'width': cwidth + 'px' }">
			<canvas ref="cvs" :width="cwidth" :height="cheight" @click="cvsClick"></canvas>
		</div>
		<el-drawer v-model="popVisble" title="操作" direction="rtl">
			<div>
				<el-form>
					<el-form-item label="画布尺寸">
						<el-slider
							v-model="imgsize"
							:max="maxsize"
							show-input
							@change="sliderChange"
							:disabled="fileList.length <= 0"
						></el-slider>
					</el-form-item>
					<el-form-item>
						<el-popover
							v-if="fileList.length <= 0"
							placement="left"
							title="提示"
							trigger="hover"
							content="请先选择一张图片"
						>
							<template #reference>
								<el-button>原图</el-button>
							</template>
						</el-popover>
						<el-button v-else @click="origin">原图</el-button>
					</el-form-item>
					<el-form-item>
						<el-popover
							v-if="fileList.length <= 0"
							placement="left"
							title="提示"
							trigger="hover"
							content="请先选择一张图片"
						>
							<template #reference>
								<el-button>取反色</el-button>
							</template>
						</el-popover>
						<el-button v-else @click="reverse">取反色</el-button>
					</el-form-item>
					<el-form-item>
						<el-popover
							v-if="curPoint.x == null"
							placement="left"
							title="提示"
							trigger="hover"
							content="请先在图中选择一个点"
						>
							<template #reference>
								<el-button>取色透明化</el-button>
							</template>
						</el-popover>
						<el-popover v-else placement="left" title="取色透明化" trigger="hover" :width="200">
							<el-form-item label="相似度">
								<el-input-number v-model="similarity" :min="0" :max="1" :step="0.1" />
							</el-form-item>
							<div style="text-align: right; margin: 0">
								<el-button size="small" @click="opacity">确定</el-button>
							</div>
							<template #reference>
								<el-button>取色透明化</el-button>
							</template>
						</el-popover>
					</el-form-item>
					<el-form-item>
						<el-popover
							v-if="fileList.length <= 0"
							placement="left"
							title="提示"
							trigger="hover"
							content="请先选择一张图片"
						>
							<template #reference>
								<el-button>下载</el-button>
							</template>
						</el-popover>
						<el-button v-else @click="downloadFile">下载</el-button>
					</el-form-item>
				</el-form>
			</div>
		</el-drawer>
		<el-button type="primary" @click="popVisble = !popVisble" circle class="fixed-btn">
			<el-icon>
				<Edit />
			</el-icon>
		</el-button>
	</div>
</template>

<script setup lang="ts">
import {
	Plus,
	Edit,
	Delete
} from '@element-plus/icons-vue'
import {
	Ref,
	UnwrapNestedRefs,
	ref,
	reactive,
	nextTick,
	onMounted
} from 'vue'
import { ElNotification } from 'element-plus'

let popVisble = ref(false),
	showPixel = ref(false),
	loading = ref(false),
	similarity = ref(0.9),
	uploader = ref(null),
	curPoint: UnwrapNestedRefs<{
		x: number | null,
		y: number | null,
		r: number | null,
		g: number | null,
		b: number | null,
		a: number | null,
		rgbaColor: String,
		hexColor: String
	}> = reactive({
		x: null,
		y: null,
		r: null,
		g: null,
		b: null,
		a: null,
		rgbaColor: '',
		hexColor: ''
	}),
	// ref画布
	cvs: Ref<HTMLCanvasElement | null> = ref(null),
	// ref容器
	baseContent: Ref<HTMLElement | null> = ref(null),
	// 图片隐藏域
	upldf: Ref<HTMLImageElement | null> = ref(null),
	// 画布宽度
	cwidth = ref(0),
	// 画布高度
	cheight = ref(0),
	// 图片实际渲染宽度
	imgsize = ref(500),
	// 最大宽度
	maxsize = ref(500),
	// 照片墙
	fileList: Ref<any[]> = ref([])
onMounted(() => {
	maxsize.value = baseContent.value!.clientWidth
})
const handleRemove = () => {
	fileList.value = []
	const context = cvs.value!.getContext('2d')
	context!.clearRect(0, 0, cwidth.value, cheight.value)
	curPoint.x = null
	curPoint.y = null
	curPoint.r = null
	curPoint.g = null
	curPoint.b = null
	curPoint.a = null
	curPoint.rgbaColor = ''
	curPoint.hexColor = ''
},
	handleChange = (f: any, fl: any[]) => {
		fileList.value = fl
		if (f && f.raw && f.raw.type !== 'image/jpeg' && f.raw.type !== 'image/png') {
			fileList.value = []
			ElNotification({
				title: '提示',
				message: '只能上传JPG,JPEG,PNG格式的图片'
			})
		}
	},
	imgLoad = (e: Event) => {
		resize(e.currentTarget as HTMLImageElement)
	},
	cvsClick = (e: MouseEvent) => {
		const x = e.offsetX
		const y = e.offsetY
		const context = cvs.value!.getContext('2d')

		const imageData = context!.getImageData(x, y, 1, 1);
		const pixel = imageData.data;
		let r = pixel[0];
		let g = pixel[1];
		let b = pixel[2];
		let a = pixel[3] / 255;
		a = Math.round(a * 100) / 100;
		let rHex = r.toString(16);
		r < 16 && (rHex = "0" + rHex);
		let gHex = g.toString(16);
		g < 16 && (gHex = "0" + gHex);
		let bHex = b.toString(16);
		b < 16 && (bHex = "0" + bHex);
		let rgbaColor = "rgba(" + r + "," + g + "," + b + "," + a + ")";
		let hexColor = "#" + rHex + gHex + bHex;
		curPoint.x = x
		curPoint.y = y
		curPoint.r = r
		curPoint.g = g
		curPoint.b = b
		curPoint.a = a
		curPoint.rgbaColor = rgbaColor
		curPoint.hexColor = hexColor
		showPixel.value = true
	},
	sliderChange = () => {
		resize(upldf.value)
	},
	resize = (i: HTMLImageElement | null) => {
		const context = cvs.value!.getContext('2d')
		const width = i!.clientWidth,
			height = i!.clientHeight
		if (width && height) {
			if (width > height) {
				cwidth.value = imgsize.value
				cheight.value = Math.floor(imgsize.value / width * height)
			} else {
				cheight.value = imgsize.value
				cwidth.value = Math.floor(imgsize.value / height * width)
			}
		} else {
			cwidth.value = imgsize.value
			cheight.value = imgsize.value
		}
		nextTick(() => {
			context!.drawImage(i as CanvasImageSource, 0, 0, cwidth.value, cheight.value)
		})
	},
	// 取反色
	reverse = () => {
		loading.value = true
		setTimeout(() => {
			const context = cvs.value!.getContext('2d')
			const imgData = context!.getImageData(0, 0, cwidth.value, cheight.value)
			// 反转颜色
			for (var i = 0; i < imgData.data.length; i += 4) {
				imgData.data[i] = 255 - imgData.data[i]
				imgData.data[i + 1] = 255 - imgData.data[i + 1]
				imgData.data[i + 2] = 255 - imgData.data[i + 2]
				imgData.data[i + 3] = 255
			}
			context!.clearRect(0, 0, cwidth.value, cheight.value)
			context!.putImageData(imgData, 0, 0)
			loading.value = false
		}, 100);
	},
	origin = () => sliderChange(),
	// 透明化
	opacity = () => {
		loading.value = true
		setTimeout(() => {
			let dots = getDots()
			if (curPoint.x == null) {
				alert("请点击图片选取想要透明化的颜色");
				return;
			}
			dots = dots.map(arr => {
				return arr.filter(item => {
					const dif = rgbaDiff({
						r1: item.r,
						g1: item.g,
						b1: item.b,
						a1: item.a
					}, {
						r2: curPoint.r,
						g2: curPoint.g,
						b2: curPoint.b,
						a2: curPoint.a
					})
					// 把不相似的像素点剔除
					return dif < similarity.value;
				})
			})
			draw(dots);
			loading.value = false
		}, 100)
	},
	draw = (dots: any[]) => {
		const ctx = cvs.value!.getContext('2d')
		ctx!.clearRect(0, 0, cwidth.value, cheight.value);
		for (let y = 0; y < dots.length; y++) {
			const yArr = dots[y];
			for (let x = 0; x < yArr.length; x++) {
				const dot = yArr[x];
				ctx!.beginPath();
				ctx!.arc(dot.x, dot.y, 1, 0, Math.PI * 2, true);
				ctx!.fillStyle = dot.color;
				ctx!.fill();
				ctx!.closePath();
			}
		}
	},
	// 下载
	downloadFile = () => {
		var bloburl = cvs.value!.toDataURL("image/png");


		var anchor = document.createElement('a');
		if ('download' in anchor) {
			anchor.style.visibility = 'hidden';
			anchor.href = bloburl;
			anchor.download = 'download.png';

			document.body.appendChild(anchor);
			var evt = document.createEvent('MouseEvents');
			evt.initEvent('click', true, true);
			anchor.dispatchEvent(evt);

			document.body.removeChild(anchor);
		} else {
			location.href = bloburl;
		}
	},
	getDots = () => {
		const dots = []
		const ctx = cvs.value!.getContext('2d')
		var imgData = ctx!.getImageData(0, 0, cwidth.value, cheight.value);
		for (var y = 0; y < imgData.height; y++) {
			const yArr = []
			//y是高，x是宽
			for (var x = 0; x < imgData.width; x++) {
				// ×4是因为rgba()
				const pixel = ctx!.getImageData(x, y, 1, 1)
				//因为img.data中包含了每个像素点的rgba，＋3表示取a的值
				if (pixel.data[3] > 0) {
					const dot: any = {}
					dot.x = x
					dot.y = y
					dot.r = pixel.data[0]
					dot.g = pixel.data[1]
					dot.b = pixel.data[2]
					dot.a = (pixel.data[3] / 255).toFixed(2)
					dot.color = "rgba(" + pixel.data[0] + ", " + pixel.data[1] + ", " + pixel.data[2] + ", " + (pixel.data[3] /
						255).toFixed(2) + ")"
					yArr.push(dot)
				}
			}
			dots.push(yArr)
		}
		return dots
	},
	// 获取相似度
	rgbaDiff = ({
		r1,
		g1,
		b1,
		a1
	}: any, {
		r2,
		g2,
		b2,
		a2
	}: any) => {
		const r3 = 1 - (Math.abs(r1 - r2) / 256);
		const g3 = 1 - (Math.abs(g1 - g2) / 256);
		const b3 = 1 - (Math.abs(b1 - b2) / 256);
		const a3 = 1 - Math.abs(Number(a1) - Number(a2));

		return (r3 + g3 + b3 + a3) / 4
	}
</script>

<style>
.avatar-uploader .el-upload {
	border: 1px dashed #d9d9d9;
	border-radius: 6px;
	cursor: pointer;
	position: relative;
	overflow: hidden;
	background-color: #ffffff;
}

.avatar-uploader .el-upload:hover {
	border-color: #409eff;
}

.avatar-uploader-icon {
	font-size: 28px;
	color: #8c939d;
	width: 178px;
	height: 178px;
	text-align: center;
}

.avatar-uploader-icon svg {
	margin-top: 74px;
	/* (178px - 28px) / 2 - 1px */
}

.avatar {
	width: 178px;
	height: 178px;
	display: block;
}

.hide .el-upload--picture-card {
	display: none;
}

.canvas-container {
	position: relative;
	margin-top: 1em;
	height: 500px;
	width: 500px;
	background-image: linear-gradient(
			45deg,
			lightgray 25%,
			transparent 25%,
			transparent 75%,
			lightgray 75%,
			lightgray
		),
		linear-gradient(
			45deg,
			lightgray 25%,
			transparent 25%,
			transparent 75%,
			lightgray 75%,
			lightgray
		);
	background-size: 40px 40px;
	background-position: 0 0, 20px 20px;
}

.color-temp {
	width: 1em;
	height: 1em;
	display: inline-block;
	border: 1px solid black;
}

.fixed-btn {
	position: fixed;
	bottom: 8em;
	right: 6em;
}
</style>
